﻿
using System;


namespace Boilen.Primitives {

    /// <summary>
    /// Provides methods for automatically formatting and reverting blocks of output text.
    /// </summary>
    public static class Enclose {

        /// <summary>
        /// Encloses the output in a four-space indent.
        /// </summary>
        public static IDisposable Indent( ICodeWriter writer ) { return Enclose.Indent( writer, null ); }
        private static IDisposable Indent( ICodeWriter writer, IDisposable outer ) {
            return new EncloseWriter(
                NoAction,
                NoAction,
                outer,
                writer.PushIndent( "    " )
            );
        }

        /// <summary>
        /// Encloses the output in an indented scope block, created using a pair of curly braces ("{" and "}").
        /// </summary>
        public static IDisposable Braces( ICodeWriter writer ) {
            return Enclose.Indent(
                writer,
                new EncloseWriter(
                    ( ) => writer.WriteLine( "{" ),
                    ( ) => writer.WriteLine( "}" )
                )
            );
        }

        /// <summary>
        /// Encloses the output in a pair of parenthesis ("(" and ")").
        /// </summary>
        public static IDisposable Parenthesis( ICodeWriter writer ) {
            return new EncloseWriter(
                ( ) => writer.Write( "(" ),
                ( ) => writer.Write( ")" )
            );
        }

        /// <summary>
        /// Encloses the output in a pair of square brackets ("[" and "]").
        /// </summary>
        public static IDisposable Brackets( ICodeWriter writer ) {
            return new EncloseWriter(
                ( ) => writer.Write( "[" ),
                ( ) => writer.Write( "]" )
            );
        }

        /// <summary>
        /// Encloses the output between a pair of new lines.
        /// </summary>
        public static IDisposable NewLine( ICodeWriter writer ) {
            return new EncloseWriter(
                ( ) => writer.WriteLine( ),
                ( ) => writer.WriteLine( )
            );
        }

        /// <summary>
        /// Encloses the output between a pair of opening and closing text lines, with optional format arguments.
        /// </summary>
        public static IDisposable Format( ICodeWriter writer, string openFormat, string closeFormat, params object[] args ) {
            Ensure.NotNull( writer, openFormat, closeFormat, args );
            return new EncloseWriter(
                ( ) => writer.WriteLine( openFormat, args ),
                ( ) => writer.WriteLine( closeFormat, args )
            );
        }

        /// <summary>
        /// Encloses the output between a pair of open and close region declarations ("#region {name}" and "#endregion").
        /// </summary>
        public static IDisposable Region( ICodeWriter writer, string name ) {
            return new EncloseWriter(
                NoAction,
                NoAction,
                Enclose.Format( writer, "#region {0}", "#endregion", name ),
                Enclose.NewLine( writer )
            );
        }


        private static void NoAction( ) { }

        private sealed class EncloseWriter : IDisposable {
            private readonly IDisposable[] outer_;
            private readonly Action close_;

            public EncloseWriter( Action open, Action close, params IDisposable[] outer ) {
                Ensure.NotNull( open, close, outer );

                this.close_ = close;
                this.outer_ = outer;
                Array.Reverse( this.outer_ );

                open( );
            }

            public void Dispose( ) {
                this.close_( );

                foreach( var item in this.outer_ )
                    using( item ) { }
            }
        }

    }

}
